package com.hsurosy.domain.strategy.service.raffle;

import com.hsurosy.domain.strategy.model.entity.StrategyAwardEntity;
import com.hsurosy.domain.strategy.model.valobj.RuleTreeVO;
import com.hsurosy.domain.strategy.model.valobj.RuleWeightVO;
import com.hsurosy.domain.strategy.model.valobj.StrategyAwardRuleModelVO;
import com.hsurosy.domain.strategy.model.valobj.StrategyAwardStockKeyVO;
import com.hsurosy.domain.strategy.repository.IStrategyRepository;
import com.hsurosy.domain.strategy.service.AbstractRaffleStrategy;
import com.hsurosy.domain.strategy.service.IRaffleAward;
import com.hsurosy.domain.strategy.service.IRaffleRule;
import com.hsurosy.domain.strategy.service.IRaffleStock;
import com.hsurosy.domain.strategy.service.armory.IStrategyDispatch;
import com.hsurosy.domain.strategy.service.rule.chain.ILogicChain;
import com.hsurosy.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import com.hsurosy.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import com.hsurosy.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Author Hsu琛君珩
 * @Date 2024-10-17 19:05
 * @Description 默认的抽奖策略实现
 * @Version: v1.0.0
 */
@Slf4j
@Service
public class DefaultRaffleStrategy extends AbstractRaffleStrategy implements IRaffleAward, IRaffleStock, IRaffleRule {

    public DefaultRaffleStrategy(IStrategyRepository strategyRepository, IStrategyDispatch strategyDispatch, DefaultChainFactory defaultChainFactory, DefaultTreeFactory defaultTreeFactory) {
        super(strategyRepository, strategyDispatch, defaultChainFactory, defaultTreeFactory);
    }

    /**
     * 责任链处理逻辑
     * 责任链处理抽奖的前置规则，例如黑名单、权重等规则
     *
     * @param userId 用户ID
     * @param strategyId 策略ID
     * @return 抽奖责任链处理后的奖品信息
     */
    @Override
    public DefaultChainFactory.StrategyAwardVO raffleLogicChain(String userId, Long strategyId) {
        // 1. 打开责任链，根据策略ID获取适用的责任链
        ILogicChain logicChain = defaultChainFactory.openLogicChain(strategyId);
        log.info("抽奖策略-责任链 userId: {} strategyId: {}", userId, strategyId);
        // 2. 执行责任链逻辑，返回处理结果
        return logicChain.logic(userId, strategyId);
    }

    /**
     * 规则树处理逻辑
     * 在责任链处理完后，调用该方法继续执行规则树过滤，如奖品次数限制、库存检查和兜底奖励等
     *
     * @param userId 用户ID
     * @param strategyId 策略ID
     * @param awardId 奖品ID，责任链处理后的奖品ID
     * @return 经过规则过滤后的最终奖品信息
     */
    @Override
    public DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId) {
        return raffleLogicTree(userId, strategyId, awardId, null);
    }

    /**
     * 带时间限制的规则树处理逻辑
     *
     * @param userId 用户ID
     * @param strategyId 策略ID
     * @param awardId 奖品ID
     * @param endDateTime 规则的截止时间
     * @return 返回规则树过滤后的奖品信息
     */
    @Override
    public DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId, Date endDateTime) {
        // 1. 从仓储中获取奖品的规则模型 rule_model
        StrategyAwardRuleModelVO strategyAwardRuleModelVO = strategyRepository.queryStrategyAwardRuleModelVO(strategyId, awardId);
        // 如果没有规则模型，直接返回当前奖品ID
        if (null == strategyAwardRuleModelVO) {
            return DefaultTreeFactory.StrategyAwardVO.builder().awardId(awardId).build();
        }

        log.info("抽奖策略-规则树 userId: {} strategyId: {} awardId: {}", userId, strategyId, awardId);

        // 2. 根据规则模型获取对应的规则树
        RuleTreeVO ruleTreeVO = strategyRepository.queryRuleTreeVOByTreeId(strategyAwardRuleModelVO.getRuleModels());
        if (null == ruleTreeVO) {
            // 如果没有找到规则树，抛出异常，表明配置中存在问题
            throw new RuntimeException("存在抽奖策略配置的规则模型 Key，未在库表 rule_tree、rule_tree_node、rule_tree_node_line 配置对应的规则树信息 " + strategyAwardRuleModelVO.getRuleModels());
        }

        // 3. 打开规则树引擎，执行规则树逻辑过滤
        IDecisionTreeEngine treeEngine = defaultTreeFactory.openLogicTree(ruleTreeVO);
        // 4. 返回规则过滤后的结果，通常是过滤后的奖品ID
        return treeEngine.process(userId, strategyId, awardId, endDateTime);
    }

    /**
     * 从队列中获取库存分配的奖品数据
     *
     * @return 抽奖奖品的库存分配数据
     * @throws InterruptedException 如果线程被中断时抛出异常
     */
    @Override
    public StrategyAwardStockKeyVO takeQueueValue() throws InterruptedException {
        // 调用仓储层，从队列中获取分配的奖品库存信息
        return strategyRepository.takeQueueValue();
    }

    /**
     * 更新指定策略的奖品库存信息
     *
     * @param strategyId 抽奖策略ID
     * @param awardId 奖品ID
     */
    @Override
    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        // 调用仓储层，更新指定策略和奖品ID对应的库存信息
        strategyRepository.updateStrategyAwardStock(strategyId, awardId);
    }

    /**
     * 查询指定策略的奖品列表
     *
     * @param strategyId 抽奖策略ID
     * @return 返回该策略对应的奖品列表
     */
    @Override
    public List<StrategyAwardEntity> queryRaffleStrategyAwardList(Long strategyId) {
        // 从仓储层查询并返回奖品列表
        return strategyRepository.queryStrategyAwardList(strategyId);
    }

    /**
     * 根据活动ID查询抽奖策略的奖品列表
     *
     * @param activityId 活动ID，用于获取对应的抽奖策略ID
     * @return 返回该抽奖策略下的奖品实体列表
     */
    @Override
    public List<StrategyAwardEntity> queryRaffleStrategyAwardListByActivityId(Long activityId) {
        Long strategyId = strategyRepository.queryStrategyIdByActivityId(activityId);
        return queryRaffleStrategyAwardList(strategyId);
    }

    /**
     * 查询奖品规则的锁定次数
     *
     * @param treeIds 规则树ID数组
     * @return 规则树ID与对应锁定次数的映射
     */
    @Override
    public Map<String, Integer> queryAwardRuleLockCount(String[] treeIds) {
        return strategyRepository.queryAwardRuleLockCount(treeIds);
    }

    /**
     * 查询权重规则
     *
     * @param strategyId 策略ID
     * @return 权重规则列表
     */
    @Override
    public List<RuleWeightVO> queryAwardRuleWeight(Long strategyId) {
        return strategyRepository.queryAwardRuleWeight(strategyId);
    }

    /**
     * 根据活动ID查询权重规则
     *
     * @param activityId 活动ID
     * @return 权重规则列表
     */
    @Override
    public List<RuleWeightVO> queryAwardRuleWeightByActivityId(Long activityId) {
        Long strategyId = strategyRepository.queryStrategyIdByActivityId(activityId);
        return queryAwardRuleWeight(strategyId);
    }

}
